"""
增强的下载模块
提供健壮的下载功能，支持暂停、取消、重试等操作
"""
import os
import time
import requests
import threading
from enum import Enum
from typing import Optional, Callable, Dict, Any
from PyQt6.QtCore import pyqtSignal, QObject

from utils import URLValidator, FileHelper, NetworkHelper
from logger import log_manager


class DownloadStatus(Enum):
    """下载状态枚举"""
    PENDING = "pending"
    DOWNLOADING = "downloading"
    PAUSED = "paused"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"


class DownloadSignals(QObject):
    """下载信号类"""
    progress_updated = pyqtSignal(str, int, int, int, float)  # task_id, progress, downloaded, total, speed
    status_changed = pyqtSignal(str, str)  # task_id, status
    download_complete = pyqtSignal(str, bool, str)  # task_id, success, file_path
    error_occurred = pyqtSignal(str, str)  # task_id, error_message


class DownloadTask:
    """下载任务类"""
    
    def __init__(self, task_id: str, url: str, save_path: str, filename: str):
        self.task_id = task_id
        self.url = url
        self.save_path = save_path
        self.filename = filename
        self.full_path = os.path.join(save_path, filename)
        
        self.status = DownloadStatus.PENDING
        self.progress = 0
        self.downloaded_bytes = 0
        self.total_bytes = 0
        self.speed = 0.0
        self.error_message = ""
        self.retry_count = 0
        self.max_retries = 3
        
        self.start_time = None
        self.end_time = None
        
        # 控制标志
        self._is_paused = False
        self._is_cancelled = False
        self._pause_event = threading.Event()
        self._pause_event.set()  # 初始状态不暂停
    
    def pause(self):
        """暂停下载"""
        self._is_paused = True
        self._pause_event.clear()
        self.status = DownloadStatus.PAUSED
    
    def resume(self):
        """恢复下载"""
        self._is_paused = False
        self._pause_event.set()
        if self.status == DownloadStatus.PAUSED:
            self.status = DownloadStatus.DOWNLOADING
    
    def cancel(self):
        """取消下载"""
        self._is_cancelled = True
        self._pause_event.set()  # 确保线程不会阻塞
        self.status = DownloadStatus.CANCELLED
    
    def is_active(self) -> bool:
        """检查任务是否处于活动状态"""
        return self.status in [DownloadStatus.DOWNLOADING, DownloadStatus.PAUSED]
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        return {
            "task_id": self.task_id,
            "url": self.url,
            "filename": self.filename,
            "save_path": self.save_path,
            "status": self.status.value,
            "progress": self.progress,
            "downloaded_bytes": self.downloaded_bytes,
            "total_bytes": self.total_bytes,
            "speed": self.speed,
            "error_message": self.error_message,
            "retry_count": self.retry_count,
            "start_time": self.start_time.isoformat() if self.start_time else None,
            "end_time": self.end_time.isoformat() if self.end_time else None
        }


class EnhancedDownloader:
    """增强的下载器类"""
    
    def __init__(self):
        self.signals = DownloadSignals()
        self.tasks: Dict[str, DownloadTask] = {}
        self.active_threads: Dict[str, threading.Thread] = {}
        self.logger = log_manager.get_logger("Downloader")
        
        # 下载配置
        self.chunk_size = 8192
        self.timeout = 30
        self.max_concurrent_downloads = 3
    
    def add_download(self, task_id: str, url: str, save_path: str, filename: str) -> bool:
        """添加下载任务"""
        try:
            # 验证URL
            if not URLValidator.is_valid_url(url):
                self.logger.error(f"无效的URL: {url}")
                return False
            
            # 清理文件名
            filename = FileHelper.sanitize_filename(filename)
            filename = FileHelper.ensure_extension(filename)
            
            # 确保保存目录存在
            if not FileHelper.ensure_directory_exists(save_path):
                self.logger.error(f"无法创建保存目录: {save_path}")
                return False
            
            # 获取安全的文件路径
            full_path = FileHelper.get_safe_path(save_path, filename)
            actual_filename = os.path.basename(full_path)
            
            # 创建下载任务
            task = DownloadTask(task_id, url, save_path, actual_filename)
            self.tasks[task_id] = task
            
            self.logger.info(f"添加下载任务: {task_id}, URL: {url}")
            return True
            
        except Exception as e:
            self.logger.error(f"添加下载任务失败: {e}")
            return False
    
    def start_download(self, task_id: str) -> bool:
        """开始下载任务"""
        if task_id not in self.tasks:
            return False
        
        task = self.tasks[task_id]
        if task.status != DownloadStatus.PENDING:
            return False
        
        # 检查并发下载限制
        active_count = len([t for t in self.tasks.values() 
                          if t.status == DownloadStatus.DOWNLOADING])
        if active_count >= self.max_concurrent_downloads:
            self.logger.warning(f"已达到最大并发下载数: {self.max_concurrent_downloads}")
            return False
        
        # 创建下载线程
        thread = threading.Thread(target=self._download_worker, args=(task_id,))
        thread.daemon = True
        self.active_threads[task_id] = thread
        thread.start()
        
        return True
    
    def pause_download(self, task_id: str) -> bool:
        """暂停下载"""
        if task_id in self.tasks:
            task = self.tasks[task_id]
            if task.status == DownloadStatus.DOWNLOADING:
                task.pause()
                self.signals.status_changed.emit(task_id, task.status.value)
                self.logger.info(f"暂停下载: {task_id}")
                return True
        return False
    
    def resume_download(self, task_id: str) -> bool:
        """恢复下载"""
        if task_id in self.tasks:
            task = self.tasks[task_id]
            if task.status == DownloadStatus.PAUSED:
                task.resume()
                self.signals.status_changed.emit(task_id, task.status.value)
                self.logger.info(f"恢复下载: {task_id}")
                return True
        return False
    
    def cancel_download(self, task_id: str) -> bool:
        """取消下载"""
        if task_id in self.tasks:
            task = self.tasks[task_id]
            if task.is_active():
                task.cancel()
                self.signals.status_changed.emit(task_id, task.status.value)
                self.logger.info(f"取消下载: {task_id}")
                
                # 删除未完成的文件
                if os.path.exists(task.full_path):
                    try:
                        os.remove(task.full_path)
                    except Exception as e:
                        self.logger.error(f"删除未完成文件失败: {e}")
                
                return True
        return False
    
    def retry_download(self, task_id: str) -> bool:
        """重试下载"""
        if task_id in self.tasks:
            task = self.tasks[task_id]
            if task.status == DownloadStatus.FAILED and task.retry_count < task.max_retries:
                # 重置任务状态
                task.status = DownloadStatus.PENDING
                task.progress = 0
                task.downloaded_bytes = 0
                task.error_message = ""
                task.retry_count += 1
                task._is_cancelled = False
                task._is_paused = False
                task._pause_event.set()
                
                self.logger.info(f"重试下载: {task_id} (第{task.retry_count}次)")
                return self.start_download(task_id)
        return False
    
    def _download_worker(self, task_id: str):
        """下载工作线程"""
        task = self.tasks[task_id]
        
        try:
            task.status = DownloadStatus.DOWNLOADING
            task.start_time = time.time()
            self.signals.status_changed.emit(task_id, task.status.value)
            
            log_manager.log_download_start(task.url, task.filename, task.save_path)
            
            # 发起HTTP请求
            headers = NetworkHelper.get_headers()
            
            # 支持断点续传
            if os.path.exists(task.full_path):
                task.downloaded_bytes = os.path.getsize(task.full_path)
                headers['Range'] = f'bytes={task.downloaded_bytes}-'
            
            response = requests.get(task.url, headers=headers, stream=True, timeout=self.timeout)
            
            # 检查响应
            if not NetworkHelper.is_valid_response(response):
                raise Exception("无效的响应或非视频内容")
            
            # 获取文件总大小
            if 'content-range' in response.headers:
                # 断点续传情况
                content_range = response.headers['content-range']
                task.total_bytes = int(content_range.split('/')[-1])
            else:
                # 普通下载
                task.total_bytes = int(response.headers.get('content-length', 0))
                if task.downloaded_bytes > 0:
                    # 服务器不支持断点续传，重新开始
                    task.downloaded_bytes = 0
            
            # 开始下载
            mode = 'ab' if task.downloaded_bytes > 0 else 'wb'
            last_time = time.time()
            last_downloaded = task.downloaded_bytes
            
            with open(task.full_path, mode) as file:
                for chunk in response.iter_content(chunk_size=self.chunk_size):
                    # 检查是否取消
                    if task._is_cancelled:
                        return
                    
                    # 检查是否暂停
                    task._pause_event.wait()
                    
                    if chunk:
                        file.write(chunk)
                        task.downloaded_bytes += len(chunk)
                        
                        # 计算进度和速度
                        if task.total_bytes > 0:
                            task.progress = int((task.downloaded_bytes / task.total_bytes) * 100)
                        
                        current_time = time.time()
                        if current_time - last_time >= 1.0:  # 每秒更新一次
                            speed = (task.downloaded_bytes - last_downloaded) / (current_time - last_time)
                            task.speed = speed
                            last_time = current_time
                            last_downloaded = task.downloaded_bytes
                            
                            self.signals.progress_updated.emit(
                                task_id, task.progress, task.downloaded_bytes, 
                                task.total_bytes, task.speed
                            )
            
            # 下载完成
            task.status = DownloadStatus.COMPLETED
            task.end_time = time.time()
            task.progress = 100
            
            self.signals.status_changed.emit(task_id, task.status.value)
            self.signals.download_complete.emit(task_id, True, task.full_path)
            
            log_manager.log_download_complete(
                task.url, task.filename, task.full_path, task.downloaded_bytes
            )
            
        except requests.exceptions.RequestException as e:
            self._handle_download_error(task, f"网络请求错误: {str(e)}")
        except Exception as e:
            self._handle_download_error(task, f"下载错误: {str(e)}")
        finally:
            # 清理线程引用
            if task_id in self.active_threads:
                del self.active_threads[task_id]
    
    def _handle_download_error(self, task: DownloadTask, error_message: str):
        """处理下载错误"""
        task.status = DownloadStatus.FAILED
        task.error_message = error_message
        task.end_time = time.time()
        
        self.signals.status_changed.emit(task.task_id, task.status.value)
        self.signals.error_occurred.emit(task.task_id, error_message)
        
        log_manager.log_download_error(task.url, error_message)
        self.logger.error(f"下载失败 {task.task_id}: {error_message}")
    
    def get_task(self, task_id: str) -> Optional[DownloadTask]:
        """获取下载任务"""
        return self.tasks.get(task_id)
    
    def get_all_tasks(self) -> Dict[str, DownloadTask]:
        """获取所有下载任务"""
        return self.tasks.copy()
    
    def remove_task(self, task_id: str) -> bool:
        """移除下载任务"""
        if task_id in self.tasks:
            task = self.tasks[task_id]
            if task.is_active():
                self.cancel_download(task_id)
            del self.tasks[task_id]
            return True
        return False